package com.klikmee.sms.core.base;

import java.text.MessageFormat;
import java.util.Date;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

import org.apache.log4j.Logger;
import org.springframework.core.task.TaskExecutor;

import com.klikmee.sms.core.base.communicator.Communicator;
import com.klikmee.sms.core.base.communicator.DeliveryReport2MessageResolver;
import com.klikmee.sms.core.base.communicator.SendSmsIntercepter;
import com.klikmee.sms.core.base.creators.MessageCreator;
import com.klikmee.sms.core.base.dao.BulkMessageDAO;
import com.klikmee.sms.core.base.dao.IncomingMessageFacadeLocal;
import com.klikmee.sms.core.base.dao.OutgoingMessageFacadeLocal;
import com.klikmee.sms.core.base.dao.ServiceFacadeLocal;
import com.klikmee.sms.core.base.exceptions.CommunicatorNotResolvedException;
import com.klikmee.sms.core.base.exceptions.DeliveryHandlerNotResolved;
import com.klikmee.sms.core.base.exceptions.DeliveryReportNotResolvedException;
import com.klikmee.sms.core.base.handlers.AbstractDeliveryHandler;
import com.klikmee.sms.core.base.handlers.DeliveryReportHandler;
import com.klikmee.sms.core.base.handlers.ExceptionHandler;
import com.klikmee.sms.core.base.postman.ServiceMessageDeliverer;
import com.klikmee.sms.core.base.util.Constants;
import com.klikmee.sms.core.base.util.States;
import com.klikmee.sms.core.commons.exceptions.ErrorInfo;
import com.klikmee.sms.core.model.BulkMessage;
import com.klikmee.sms.core.model.IncomingMessage;
import com.klikmee.sms.core.model.OutgoingMessage;
import com.klikmee.sms.core.model.PaymentInvoice;
import com.klikmee.sms.core.model.Provider;
import com.klikmee.sms.core.model.Service;
import com.klikmee.sms.core.model.ServiceChargingCategory;

public class Core {
	/**
	 * Instance of log
	 */
	private Logger										logger									= Logger.getLogger(Core.class);
	/**
	 * Dao instances
	 */
	private OutgoingMessageFacadeLocal					outgoingMessageDAO						= null;
	private IncomingMessageFacadeLocal					incomingMessageDAO						= null;
	private ServiceFacadeLocal							serviceDAO								= null;
	private BulkMessageDAO								bulkMessageDAO							= null;
	
	/**
	 * Task executor. Used to execute tasks.
	 */
	private TaskExecutor								taskExecutor							= null;

	/**
	 * Message creator used to create charged and bulk messages
	 */
	private MessageCreator								messageCreator							= null;
	/**
	 * Component responsible for delivering messages, status resports and subscription events to service instances
	 */
	private ServiceMessageDeliverer						messageDeliverer						= null;
	
	/**
	 * Cached list off all services for better performance
	 */
	private List<Service>								servicesList							= null;

	/**
	 * Service instance used in situations when customer needs to be notified free of charge
	 */
	private Service										freeOfChargeService						= null;
	private String										notEngoughCreditMessageText				= null;
	private String										serviceNotAvailableMessageText			= null;
	
	/**
	 * Service instance used in situations when customer sends unrecognized request
	 */
	private Service										badRequestService						= null;
	private String										badSintaxMessageText					= null;
	// misc
	
	/**
	 * Ids used to load badRequest and freeOfCharge services
	 */
	private Long										badRequestServiceId						= null;
	private Long										freeOfChargeServiceId					= null;

	/**
	 * Queues for buffering incoming, outgoing, bulk and delivery report
	 * messages.
	 */
	private BlockingQueue<IncomingMessage>				incomingMessageQueue					= new LinkedBlockingQueue<IncomingMessage>();
	private BlockingQueue<OutgoingMessage>				outgoingMessageQueue					= new LinkedBlockingQueue<OutgoingMessage>();
	private BlockingQueue<OutgoingMessage>				statusReportQueue						= new LinkedBlockingQueue<OutgoingMessage>();
	private BlockingQueue<BulkMessage>					bulkMessageQueue						= new LinkedBlockingQueue<BulkMessage>();

	/**
	 * Worker threads for queue processing.
	 */
	private Thread										incommingMessageQueueProcessingThread	= null;
	private Thread										outgoingMessageQueueProcessingThread	= null;
	private Thread										statusReportQueueProcessingThread		= null;
	private Thread										bulkMessageQueueProcessingThread		= null;

	/**
	 * Exception handler used to handler internal, service and other errors
	 */
	private ExceptionHandler							exceptionHandler						= null;
	/**
	 * Map of communicators configured for this instance of core context. Key is
	 * the name of the communicator identical to the value of
	 * {@link Provider#getName()} of the provider the communicator is
	 * implemented for. The value is and instance of communicator for that
	 * provider. Example: for MTS communicator value is MTS for VIP is VIP and
	 * so on The names of each implemented communicator can be found in
	 * {@link Constants} class.
	 * 
	 */
	private Map<String, Communicator>					communicatorMap							= null;
	/**
	 * Map of delivery report resolvers used to resolve delivery report to send
	 * message. Every {@link DeliveryReport2MessageResolver} instance is mapped
	 * to provider's unique name.
	 */
	private Map<String, DeliveryReport2MessageResolver>	deliveryReportResolverMap				= null;

	/**
	 * Map of delivery report handlers used to update status reports of sent
	 * messages depending on the type of sent message.
	 */
	private Hashtable<Integer, DeliveryReportHandler>	deliveryReportHandlerMap				= null;
	/**
	 * Flag indicating that is time to stop queue processing.
	 */
	private boolean										stopQueueProcessing						= false;

	/**
	 * Default constructor for core context
	 */
	public Core() {
	}

	/**
	 * Init method for initializing core resources
	 */
	public void init() {
		// cache all service definitions
		loadServicesDefinition();
		// start all worker threads
		this.startQueueProcessingThreads();
		logger.info("Core initialized. ");
	}

	/**
	 * Enqueues incoming message for processing.
	 * <p>
	 * Usually called from {@link Communicator} implementation
	 * </p>
	 * 
	 * @param incomingMessage
	 */
	public void enqueueIncomingMessage(IncomingMessage incomingMessage) {
		try {
			incomingMessageQueue.put(incomingMessage);
		} catch (InterruptedException e) {
			logger.fatal("Interupted while waiting to enqueue incoming message. ", e);
		} catch (Exception e) {
			exceptionHandler.handle(e);
		}
	}

	/**
	 * Enqueues new message to the outgoing queue.
	 * @param outgoingMessage a message to be sent
	 */
	public void enqueueOutgoingMessage(OutgoingMessage outgoingMessage) {
		try {
			outgoingMessageQueue.put(outgoingMessage);
		} catch (InterruptedException e) {
			logger.fatal("Interupted while waiting to enqueue outgoing message. ", e);
		}
	}

	/**
	 * Adds new bulk message to the tail of the outgoing bulk queue
	 * @param bulkMessage
	 */
	public void enqueueBulkMessage(BulkMessage bulkMessage) {
		try {
			this.bulkMessageQueue.put(bulkMessage);
		} catch (InterruptedException e) {
			logger.fatal("Interupted while waiting to enqueue bulk message. ", e);
		}
	}

	/**
	 * Enqueues status report for the send message. Usually called by
	 * communicator.
	 * 
	 * @param outgoingMessage
	 */
	public void enqueueStatusReport(OutgoingMessage outgoingMessage) {
		try {
			this.statusReportQueue.put(outgoingMessage);
		} catch (InterruptedException e) {
			logger.fatal("Interupted while waiting to enqueue status message. ", e);
		}
	}

	/**
	 * Processes an incoming message from the queue by calling
	 * {@link #onNewMessage(IncomingMessage)} method
	 */
	public void processIncomingMessageQueue() {

		try {

			IncomingMessage incomingMessage = incomingMessageQueue.take();
			this.onNewMessage(incomingMessage);

		} catch (InterruptedException e) {
			logger.fatal("Interupted while waiting to process incoming messages ", e);
		} catch (Exception e) {
			exceptionHandler.handle(e);
		}
	}

	/**
	 * Takes next message from the outgoing queue and calls
	 * {@link #forwardToCommunicator(OutgoingMessage)} with the message as an
	 * argument.
	 */
	public void processOutgoingMessageQueue() {

		try {

			OutgoingMessage outgoingMessage = outgoingMessageQueue.take();
			this.forwardToCommunicator(outgoingMessage);

		} catch (InterruptedException e) {
			logger.fatal("Interupted while waiting to process outgoing messages ", e);
		} catch (Exception e) {
			exceptionHandler.handle(e);
		}

	}

	/**
	 * Takes next bulk message from queue and forwards it to the resolved
	 * communicator
	 */
	public void processBulkMessageQueue() {

		try {

			BulkMessage bulkMessage = bulkMessageQueue.take();
			bulkMessage.setSendingTime(new Date());
			forwardToCommunicator(bulkMessage);
		} catch (InterruptedException e) {
			logger.fatal("Interupted while waiting to process bulk messages ", e);
		} catch (Exception e) {
			logger.fatal("Unexpected error while processing bulk queue ", e);
		}
	}

	/**
	 * <p>
	 * Takes next status report from the queue and processes it by calling
	 * {@link #onDeliveryReceived(OutgoingMessage)}
	 * </p>
	 */
	public void processStatusReportQueue() {

		try {
			OutgoingMessage message = this.statusReportQueue.take();
			this.onDeliveryReceived(message);

		} catch (InterruptedException e) {
			logger.fatal("Interupted while waiting to process status report.", e);
		} catch (Exception e) {
			exceptionHandler.handle(e);
		}
	}

	/**
	 * Forwards bulk message to appropriate communicator
	 * 
	 * @param bulkMessage
	 */
	private void forwardToCommunicator(BulkMessage bulkMessage) {
		// resolve communicator
		Provider provider = bulkMessage.getProvider();
		try {
			Communicator communicator = resolveCommunicator(provider);

			communicator.sendBulkMessage(bulkMessage);

			logger.info(MessageFormat.format("Bulk message with id = [{0}] forwarded to communicator", bulkMessage.getId()));
		} catch (CommunicatorNotResolvedException e) {
			exceptionHandler.handle(e);
		} catch (Exception e) {
			exceptionHandler.handle(e);
		}

	}

	/**
	 * Sends the message via appropriate communicator. Communicator is resolved
	 * from {@link #communicatorMap} and each {@link SendSmsIntercepter} for
	 * that communicator is executed. Finaly message is sent by calling
	 * {@link Communicator#sendMessage(OutgoingMessage)} on the resolved
	 * communicator.
	 * 
	 * @param outgoingMessage
	 */
	private void forwardToCommunicator(OutgoingMessage outgoingMessage) {
		// resolve communicator used to send this message
		Communicator communicator = null;
		try {
			communicator = resolveCommunicator(outgoingMessage.getProvider());
			// execute intercepters assigned to this communicator, if any
			List<SendSmsIntercepter> smsIntercepters = communicator.getSendSmsIntercepters();

			for (SendSmsIntercepter intercepter : smsIntercepters) {
				intercepter.processMessage(outgoingMessage);
			}

			// forward message to communicator
			communicator.sendMessage(outgoingMessage);
			logger.info(MessageFormat.format("Message with id = [{0}] sent to {1} communicator", outgoingMessage.getId(), communicator));
		} catch (CommunicatorNotResolvedException e) {
			exceptionHandler.handle(e);
		}

	}

	/**
	 * Helper method for constructing vip billing Id for charged messages.
	 * 
	 * @param referenceId
	 * @param sequenceNumber
	 * @return referenceId appended by the sequenceId
	 */
	public String generateVipBillingId(String referenceId, Long sequenceNumber) {

		StringBuilder builder = new StringBuilder(referenceId);
		// check if billing id has already been set
		int indexOfMinus = referenceId.indexOf("-");
		if (indexOfMinus != -1) {
			// in this case remove number after - and set a new one
			builder.replace(indexOfMinus + 1, indexOfMinus + 2, sequenceNumber.toString());
		} else {
			// just append - [seq number]
			builder.append("-").append(sequenceNumber.toString());
		}

		return builder.toString();
	}

/**
     * callback from {@link #sendMessage(com.klikmee.sms.core.entities.OutgoingMessage)
     * @param outgoingMessage - should be same object as it was in method sendMessage
     * 
     */
	public void onMessageSent(OutgoingMessage outgoingMessage) {

		if (outgoingMessage == null) {
			logger.fatal("Could not process null core.OnMessageSent(), returning");
			return;
		}

		// set outgoing message state
		outgoingMessage.setState(States.OutgoingMessage.SENT);
		outgoingMessageDAO.edit(outgoingMessage);

		logger.debug(String.format("Message successfully sent id = [%s]", outgoingMessage.getId()));
	}

	/**
	 * Invoked by some of the communicators after successfully sending message.
	 * 
	 * @param bulkMessage
	 *            bulk message sent by communicator.
	 */

	public void onBulkMessageSent(BulkMessage bulkMessage) {
		// verify message
		if (bulkMessage == null) {
			return;
		}

		// just update state
		bulkMessage.setState(States.BulkMessage.SENT);

		bulkMessageDAO.edit(bulkMessage);

	}

	/**
	 * Callback from communicator when it receives new message. This method
	 * links the message to the service based on it's syntax. If massage has a
	 * wrong syntax it is linked to bad request service.
	 * 
	 * @param sms
	 * 
	 */
	public void onNewMessage(IncomingMessage newMessage) {

		// resolve service for this message
		Service resolvedService = resolveService(newMessage);

		// if message could not be resolve to any service link message to the
		// bad request service and send info message
		// back to the user
		if (resolvedService == null) {
			newMessage.setService(badRequestService);
			newMessage.setState(States.IncommingMessage.BAD_SYNTAX_NOTIFICATION_SENT);
			incomingMessageDAO.create(newMessage);

			ServiceChargingCategory serviceChargingCategory = getBadRequestService().getServiceChargingCategoryList().iterator().next();
			messageCreator.createAndEnqueueResponse(newMessage, serviceChargingCategory, badSintaxMessageText);
		}

		// if service for this message is resolved,
		// forward the message to the concrete service instance
		else {
			newMessage.setService(resolvedService);
			newMessage.setState(States.IncommingMessage.SYNTACTICALLY_OK);
			incomingMessageDAO.create(newMessage);

			// try to deliver message to service
			boolean messageDelivered = messageDeliverer.deliverMessage(newMessage);

			if (!messageDelivered) {
				// if message is not delivered to service, send info message to
				// user

				messageCreator.createAndEnqueueResponse(newMessage, this.getFreeOfChargeService().getServiceChargingCategoryList().iterator().next(), this.serviceNotAvailableMessageText);
				// and set im state
				newMessage.setState(States.IncommingMessage.SERVICE_NOT_AVAILABLE_NOTIFICATION_SENT);
				incomingMessageDAO.edit(newMessage);
			}

		}

	}

	/**
	 * Callback from communicator when new delivery is received. Step 1: link
	 * delivery report, represented by {@link OutgoingMessage Outgoing message}
	 * to sent message, by examining provider field. If delivery could not be
	 * linked to sent message return and log this situation.
	 * 
	 * Step 2: if sent message does not have the payment invoice set it's status
	 * to delivered and return.
	 * 
	 * Step 3: Resolve payment invoice type
	 * 
	 * 
	 * @param deliveredMessage
	 * @throws DeliveryReportNotResolvedException
	 * @throws DeliveryHandlerNotResolved 
	 */
	public void onDeliveryReceived(OutgoingMessage deliveredMessage) throws DeliveryReportNotResolvedException, DeliveryHandlerNotResolved {

		// get delivery report resolver for privier
		DeliveryReport2MessageResolver deliveryReport2MessageResolver = deliveryReportResolverMap.get(deliveredMessage.getProvider().getName());
		
		// this the actual message to be found using data from parameter
		OutgoingMessage sentMessage = deliveryReport2MessageResolver.resolve(deliveredMessage);

		// set status report
		sentMessage.setDeliveryStatus(deliveredMessage.getDeliveryStatus());

		// get payment invoice
		PaymentInvoice paymentInvoice = sentMessage.getPaymentInvoiceList().iterator().next();

		// get handler for this PI type
		DeliveryReportHandler deliveryHandler = deliveryReportHandlerMap.get(paymentInvoice.getType());
		// if handler is available, handle message
		if (deliveryHandler != null) {

			deliveryHandler.handle(sentMessage);

		} else {
			// throw exception
			DeliveryHandlerNotResolved deliveryHandlerNotResolvedEx = new DeliveryHandlerNotResolved(String.format("Cound not resolve status report handler for outgoing message id = [%s]", sentMessage.getId()));
			ErrorInfo errorInfo = deliveryHandlerNotResolvedEx.addInfo();
			errorInfo.getParameters().put("PaymentInvoiceType", paymentInvoice.getType());
			
			throw deliveryHandlerNotResolvedEx;
		}
	}

	/**
	 * Resolves service for the received message. List of all registered
	 * services searched for the one that matches providers short code and
	 * message sintax.
	 * 
	 * @param newMessage
	 *            the newly received message to be matched with the service.
	 */
	private Service resolveService(IncomingMessage newMessage) {
		// this is to be returned as result
		Service result = null;

		for (Service service : servicesList) {

			// check if new message's TO number is equal to service phone number
			if ((newMessage.getProvider().getName().equals(Constants.PROVIDER_MTS) && newMessage.getToNumber().matches(service.getMTSShortCode()))
					|| (newMessage.getProvider().getName().equals(Constants.PROVIDER_TELENOR) && newMessage.getToNumber().matches(service.getTelenorShorCode()))
					|| (newMessage.getProvider().getName().equals(Constants.PROVIDER_VIP) && newMessage.getToNumber().matches(service.getVIPShortCode()))
					|| (newMessage.getProvider().getName().equals(Constants.PROVIDER_MTEL) && newMessage.getToNumber().matches(service.getmTelShortCode()))) {
				// check if new message match service's syntax
				if (newMessage.getMessage().matches(service.getSintax())) {
					// set the result
					result = service;
				}
			}
		}

		return result;
	}

	/**
	 * Resolves an instance of concrete communicator from
	 * {@link #communicatorMap} to be used to send outgoing message. Matching is
	 * done using {@link Provider#getName()} attached to the outgoing message
	 * 
	 * @param provider
	 *            the actual message to be sent
	 * @return an instance of {@link Communicator} used to send the message, or
	 *         null if no communicator could be resolved
	 * @throws CommunicatorNotResolvedException
	 */
	private Communicator resolveCommunicator(Provider provider) throws CommunicatorNotResolvedException {

		// get the name of provider associated with this message
		String providersName = provider.getName();

		// use this value to get instance of communicator from the map
		Communicator communicator = this.communicatorMap.get(providersName);

		if (communicator == null) {
			throw new CommunicatorNotResolvedException(MessageFormat.format("Failed to resolve communicator for message id = [{0}] by providers name = {1}", provider.getId(), providersName));
		}
		return communicator;
	}

	/**
	 * Starts threads for queue processing
	 */
	private void startQueueProcessingThreads() {
		// incoming message queue

		this.incommingMessageQueueProcessingThread = new Thread(new Runnable() {

			public void run() {
				while (!stopQueueProcessing) {
					Core.this.processIncomingMessageQueue();
				}

			}
		});

		this.incommingMessageQueueProcessingThread.start();
		logger.info("Started processing incoming message queue...");

		// outgoing message queue
		this.outgoingMessageQueueProcessingThread = new Thread(new Runnable() {

			public void run() {
				while (!stopQueueProcessing) {
					Core.this.processOutgoingMessageQueue();
				}

			}
		});

		this.outgoingMessageQueueProcessingThread.start();
		logger.info("Started processing outgoing message queue...");

		// status report queue
		this.statusReportQueueProcessingThread = new Thread(new Runnable() {

			public void run() {
				while (!stopQueueProcessing) {
					Core.this.processStatusReportQueue();
				}

			}
		});
		this.statusReportQueueProcessingThread.start();
		logger.info("Started processing status report queue...");
		// status report queue

		this.bulkMessageQueueProcessingThread = new Thread(new Runnable() {

			public void run() {
				while (!stopQueueProcessing) {
					Core.this.processBulkMessageQueue();
				}

			}
		});

		this.bulkMessageQueueProcessingThread.start();
		logger.info("Started processing bulk message queue...");

	}

	/*
	 * utility method for loading service definitions from db. Loads all
	 * services plus bad request and free of charge service.
	 */
	public void loadServicesDefinition() {

		badRequestService = serviceDAO.find(this.badRequestServiceId);
		freeOfChargeService = serviceDAO.find(this.freeOfChargeServiceId);
		servicesList = serviceDAO.findAll();
		logger.info("Loaded " + servicesList.size() + " service definitions from database.");
	}

	public void destroy() {
		stopQueueProcessing = true;
		// interupt the threads
		this.incommingMessageQueueProcessingThread.interrupt();
		this.outgoingMessageQueueProcessingThread.interrupt();
		this.bulkMessageQueueProcessingThread.interrupt();
		this.statusReportQueueProcessingThread.interrupt();

		logger.info("Shuting down core completed.");
	}

	public List<Service> getServicesList() {
		return servicesList;
	}

	public Service getFreeOfChargeService() {
		return freeOfChargeService;
	}

	public Service getBadRequestService() {
		return badRequestService;
	}

	public void setOutgoingMessageDAO(OutgoingMessageFacadeLocal outgoingMessageDAO) {
		this.outgoingMessageDAO = outgoingMessageDAO;
	}

	public String getNotEngoughCreditMessageText() {
		return notEngoughCreditMessageText;
	}

	public void setDeliveryReportHandlerMap(Hashtable<Integer, DeliveryReportHandler> deliveryReportHandlerMap) {
		this.deliveryReportHandlerMap = deliveryReportHandlerMap;
	}

	public void setServiceDAO(ServiceFacadeLocal serviceDAO) {
		this.serviceDAO = serviceDAO;
	}

	public void setMessageCreator(MessageCreator billing) {
		this.messageCreator = billing;
	}

	public void setIncomintMessageDAO(IncomingMessageFacadeLocal incomintMessageDAO) {
		this.incomingMessageDAO = incomintMessageDAO;
	}

	public void setMessageDeliverer(ServiceMessageDeliverer messageDeliverer) {
		this.messageDeliverer = messageDeliverer;
	}

	public void setBadSintaxMessageText(String badSintaxMessageText) {
		this.badSintaxMessageText = badSintaxMessageText;
	}

	public void setServiceNotAvailableMessageText(String serviceNotAvailableMessageText) {
		this.serviceNotAvailableMessageText = serviceNotAvailableMessageText;
	}

	public void setBulkMessageDAO(BulkMessageDAO bulkMessageDAO) {
		this.bulkMessageDAO = bulkMessageDAO;
	}

	public void setNotEngoughCreditMessageText(String notEngoughCreditMessageText) {
		this.notEngoughCreditMessageText = notEngoughCreditMessageText;
	}

	public void setTaskExecutor(TaskExecutor taskExecutor) {
		this.taskExecutor = taskExecutor;
	}

	public String getBadSintaxMessageText() {
		return badSintaxMessageText;
	}

	public OutgoingMessageFacadeLocal getOutgoingMessageDAO() {
		return outgoingMessageDAO;
	}

	public IncomingMessageFacadeLocal getIncomingMessageDAO() {
		return incomingMessageDAO;
	}

	public ServiceFacadeLocal getServiceDAO() {
		return serviceDAO;
	}

	public BulkMessageDAO getBulkMessageDAO() {
		return bulkMessageDAO;
	}

	public TaskExecutor getTaskExecutor() {
		return taskExecutor;
	}

	public MessageCreator getMessageCreator() {
		return messageCreator;
	}

	public ServiceMessageDeliverer getMessageDeliverer() {
		return messageDeliverer;
	}

	public String getServiceNotAvailableMessageText() {
		return serviceNotAvailableMessageText;
	}

	public Logger getLogger() {
		return logger;
	}

	public BlockingQueue<IncomingMessage> getIncomingMessageQueue() {
		return incomingMessageQueue;
	}

	public BlockingQueue<OutgoingMessage> getOutgoingMessageQueue() {
		return outgoingMessageQueue;
	}

	public BlockingQueue<OutgoingMessage> getStatusReportQueue() {
		return statusReportQueue;
	}

	public BlockingQueue<BulkMessage> getBulkMessageQueue() {
		return bulkMessageQueue;
	}

	public Thread getIncommingMessageQueueProcessingThread() {
		return incommingMessageQueueProcessingThread;
	}

	public Thread getOutgoingMessageQueueProcessingThread() {
		return outgoingMessageQueueProcessingThread;
	}

	public Thread getStatusReportQueueProcessingThread() {
		return statusReportQueueProcessingThread;
	}

	public Thread getBulkMessageQueueProcessingThread() {
		return bulkMessageQueueProcessingThread;
	}

	public boolean isStopQueueProcessing() {
		return stopQueueProcessing;
	}

	public Long getBadRequestServiceId() {
		return badRequestServiceId;
	}

	public void setBadRequestServiceId(Long badRequestServiceId) {
		this.badRequestServiceId = badRequestServiceId;
	}

	public Long getFreeOfChargeServiceId() {
		return freeOfChargeServiceId;
	}

	public void setFreeOfChargeServiceId(Long freeOfChargeServiceId) {
		this.freeOfChargeServiceId = freeOfChargeServiceId;
	}

	public void setExceptionHandler(ExceptionHandler exceptionHandler) {
		this.exceptionHandler = exceptionHandler;
	}

	public void setDeliveryReportResolverMap(Map<String, DeliveryReport2MessageResolver> deliveryReportResolverMap) {
		this.deliveryReportResolverMap = deliveryReportResolverMap;
	}
	
	public Hashtable<Integer, DeliveryReportHandler> getDeliveryReportHandlerMap() {
		return deliveryReportHandlerMap;
	}
	
	public void setCommunicatorMap(Map<String, Communicator> communicatorMap) {
		this.communicatorMap = communicatorMap;
	}
}
